// Symbol只能是string|number|undefined
const s0 = Symbol("0")
const s1 = Symbol("0")
console.log(s0.toString()); // Symbol(0)
console.log(Boolean(s0)); // true

const n0 = Symbol(0)
console.log(n0.toString()); // Symbol(0)
console.log(Boolean(n0)); // true

const und = Symbol(undefined) // 和Symbol()是一样的效果
console.log(und); // Symbol()


const info = {
    [s0]: "name",
    test: 123
}
console.log(info[s0]); // 直接访问
// console.log(info2[s1]); // js:undefined TS:×

// 无法访问
console.log(Object.keys(info)); // ["test"]
console.log(Object.getOwnPropertyNames(info)); // ["test"]
console.log(JSON.stringify(info)); // {"test":123}

// 可以访问
console.log(Object.getOwnPropertySymbols(info)); // [Symbol(0)]
console.log(Reflect.ownKeys(info)); // ["test", Symbol(0)]


/**
 * Symbol 的两个方法
 * Symbol.for() // 如果使用过for创建过同名标识符，始终返回该Symbol，没有找到则创建Symbol
 * Symbol.keyFor() // 返回通过Symbol.key()创建的key(标识符)
 */

const sb1 = Symbol.for("sb")
console.log(sb1 === Symbol.for("sb")); // true

const sb2 = Symbol("sb")
console.log(sb2 === Symbol.for("sb")); // false

console.log(Symbol.keyFor(sb2)); // undefined
console.log(Symbol.keyFor(sb1)); // sb

/**
 * 系统内置Symbol
 * Symbol.hasInstance // 使用instanceof的时候执行该方法，形参为需要对比的其它对象
 * Symbol.isConcatSpreadable // 布尔值，数组执行concat方法时是否需要拆开
 * Symbol.species // js有区别，ts默认 // map循环后的返回
 * Symbol.match // [replace,search,search,split] // 正则相关
 * Symbol.iterator // 迭代器
 * Symbol.toPrimitive // 被转换为原始对象时运行
 * Symbol.toStringTag
 * Symbol.unscopables 从with中排除属性让其无法再with中获取属性
 */

// 1.Symbol.hasInstance
const obj = {
    [Symbol.hasInstance](otherObj: any) {
        console.log(otherObj); // { a: 1 }
        return true
    }
}
console.log({ a: 1 } instanceof (obj as any)); // 打印返回的bool值 true


// 2.Symbol.isConcatSpreadable
let arr: any = [1, 2]
let arr2 = [3, 4]

console.log(arr.concat(arr2)); // [1,2,3,4]
arr[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2)); // [[1,2,Symbol(Symbol.isConcatSpreadable): false],3,4]


// 3.Symbol.species
class Array1 extends Array {
    constructor(...args: any) { super(...args) }
    static get [Symbol.species]() { return Array; } // js有区别，ts默认
}

const a = new Array1(1, 2, 3);
console.log(a instanceof Array1);
const mapped = a.map(x => x * x);
console.log(mapped instanceof Array1);
// expected output: false
console.log(mapped instanceof Array);
// expected output: true

// 4.Symbol.match //正则系列
// Symbol.replace
// Symbol.search
// Symbol.split
// Symbol.match
// Symbol.matchAll // node 不支持
let regObj = {
    [Symbol.match](str: string) {
        console.log(str.length)
    }
}
"abcde".match(regObj as any)


// 5.Symbol.iterator
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols

let someString = "hi";
console.log(typeof someString[Symbol.iterator]);          // "function"
let iterator = someString[Symbol.iterator]();
console.log(iterator + "");                               // "[object String Iterator]"
console.log(iterator.next());                             // { value: "h", done: false }
console.log(iterator.next());                             // { value: "i", done: false }
console.log(iterator.next());                             // { value: undefined, done: true }

// 6. Symbol.toPrimitive
const objToPrimitive: unknown = {
    [Symbol.toPrimitive](type: string) {
        console.log(type); // 被转换为原始对象时运行
        return '[objToPrimitive]'
    }
}
console.log((objToPrimitive as number) + "");

// 7.Symbol.toStringTag
let objToStr = {
    [Symbol.toStringTag]: "abc"
}
console.log(objToStr.toString()); // [object abc]


// 8.Symbol.unscopables 从with中排除属性让其无法再with中获取属性
const object1: any = {
    property1: 42
};
object1[Symbol.unscopables] = {
    property1: true
}
// with (object1) { // 严格模式下不支持with
//     console.log(property1);
//     // expected output: Error: property1 is not defined
// }
console.log(Object.keys(Array.prototype[Symbol.unscopables])); // 查看哪些被过滤


/**
 * 用于定义(模块中)类的私有属性[js定义]
 */
const func1 = Symbol("func1")
// tslint:disable-next-line: max-classes-per-file
class JsPrivateTest {
    [func1]() {
        console.log();
    }
}